package scanner

import (
	"context"
	"strings"
	"sync"

	"gitee.com/linxing_3/sr-core/plugins/log"
)

type IScanner interface {
	// Scan 扫描文件
	Scan(ctx context.Context, opts ...Option) error

	// GetJobStates 获取扫描状态
	GetJobStates() []*ScanState

	// Start 启动扫描器服务
	Start(ctx context.Context) error
}

func NewScanner(handler IFileHandler, opts ...Option) IScanner {
	cfg := defaultScanOption()
	for _, opt := range opts {
		opt(cfg)
	}

	return &Scanner{
		opt:      *cfg,
		jobQueue: make(chan *scanJob, cfg.QueueLen),
		repo:     handler,
		jobState: &sync.Map{},
	}

}

type Scanner struct {
	opt  ScanOption   // 扫描选项
	repo IFileHandler // 文件处理器

	jobQueue chan *scanJob
	jobState *sync.Map
}

func (s *Scanner) storeState(path []string, job *scanJob) {
	key := strings.Join(path, "::")
	s.jobState.Store(key, job)
}

func (s *Scanner) delState(path []string) {
	key := strings.Join(path, "::")
	s.jobState.Delete(key)
}

func (s *Scanner) Scan(ctx context.Context, opts ...Option) error {
	n := s.opt
	nopt := &n
	for _, opt := range opts {
		opt(nopt)
	}

	scanJob := NewScanJob(nopt, s.repo)
	s.storeState(nopt.Paths, scanJob)

	if nopt.Async {
		go func() {
			s.jobQueue <- scanJob
		}()
		return nil
	} else {
		s.jobQueue <- scanJob
	}

	return nil
}

func (s *Scanner) Start(ctx context.Context) error {
	go func() {
		for {
			select {
			case job := <-s.jobQueue:
				log.Debugf("start scan job: %s", job.opt.Paths)
				if err := job.Execute(ctx); err != nil {
					log.Errorf("scan job error: %s", err)
				} else {
					s.delState(job.opt.Paths)
				}
			case <-ctx.Done():
				return
			}
		}
	}()

	return nil
}

func (s *Scanner) GetJobStates() []*ScanState {
	states := make([]*ScanState, 0)
	s.jobState.Range(func(key, value any) bool {
		states = append(states, value.(*scanJob).GetState())
		return true
	})

	return states
}
